Repository navigation
Editor window: worktree support (chat.editorWindow.enableWorktree) - #348306
Sandeep Somavarapu (sandy081) wants to merge 269 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Outstanding worktree moves can leave the folder out of sync with a newly opened local chat, while explicitly revealing a restored chat can fail to trigger folder following.
2 open issues
What changed in this PR
Makes an editor window’s single local folder follow the chat the user opens, behind the experimental chat.editorWindow.enableWorktree setting. This builds on #348304’s worktree-location support and distinguishes explicit opens from restored chats.
Changes:
- Exposes the shown session and its open/restore reason through the chat host service.
- Follows local agent-host sessions to their working directories and returns to the primary folder for local chats.
- Adds tests for host switching, deferred session discovery, and setting-gated behavior.
| File | Description |
|---|---|
src/vs/workbench/services/workspaces/common/gitFolderCheckout.ts |
Prerequisite: shared Git checkout reader. |
src/vs/workbench/contrib/sessions/browser/workspaceFolderRepositories.ts |
Prerequisite: uses the shared checkout reader. |
src/vs/workbench/contrib/chat/test/browser/widgetHosts/viewPane/chatViewHostService.test.ts |
Tests shown-session reporting across hosts. |
src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHost/agentHostWorktreeLocations.test.ts |
Prerequisite: tests worktree location validation. |
src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHost/agentHostWorktreeFollow.test.ts |
Tests folder following and setting gates. |
src/vs/workbench/contrib/chat/common/constants.ts |
Prerequisite: defines the worktree setting key. |
src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts |
Publishes shown-session state and restore reasons. |
src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewHostService.ts |
Exposes the active host’s shown session. |
src/vs/workbench/contrib/chat/browser/chatViewHost.ts |
Defines the shown-session observable contract. |
src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.contribution.ts |
Loads worktree location and follow contributions. |
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostWorktreeLocations.ts |
Prerequisite: provides validated worktree locations. |
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostWorktreeFollow.ts |
Moves the folder in response to opened chats. |
Review effort: Balanced
…ns-control' into sandy081/agents/editor-window-chat-part
…rktree-locations' into sandy081/agents/editor-window-chat-part # Conflicts: # src/vs/workbench/contrib/chat/browser/chatViewHost.ts # src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewHostService.ts
… folder picker sections The Folder (or Folders) and Worktrees sections of a composer restricted to a window's folders, their order and their labels (Current, New Worktree) now come from folderLocationSections (#348304), which the Chat view's folder chip shares, rather than from rules of its own. The picker shows the same items as before. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… not one that the user loads meanwhile The Chat view marked every chat it showed while its restore was under way as restored, also a chat that the user loaded meanwhile and that superseded the restore. It now passes the reason with the chat it shows, and reports it only while it sets that chat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, worktrees inside the folder, and stops on dispose and when off Review fixes for worktree support: - Any chat the user brings up, also one that moves nothing, no chat, and a move of the folder that is not its own supersede every switch asked for before, also a switch of a chat that the user opened. - A worktree inside the folder is a location to follow; a folder inside the folder's own location that is no worktree works there. - The working directory that a restored chat is first known to work in, as the sessions list loads, is where it works, not a change to follow; only a chat that the user opened follows it. - As it is disposed, no switch under way applies, what switches read is cancelled, and the terminals it marked are not marked. - With the setting off, it marks no terminals; as it turns off, it clears their marks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Moves the chat part that the Sessions view feature and the sessions in the Chat view feature each carried into one setting-less service, inert until a feature enables it:
- IEditorWindowChatPartService.enable({ anchorViewId, anchorPriority, enabled }) creates the part before the grid, docks it beside the anchor of the highest priority, and registers its actions and the Changes editor. While enabled, it restores with the window, replaces the Chat view through the chat part's IChatViewHost and presents the window's sessions; while not, it does not restore and hides.
- AgentsChatViewHost leaves chats it cannot show, such as local chat, to the Chat view (canShow), opens New Chat as a composer, reports the active tab's chat as shownSession ('restore' for the tabs restore() showed, else 'open'), and stops opening the chat part once deactivated.
- IAgentsChatPartService gets shownSession and a composer folder seam (setComposerFolderOptions), the window's folders by default.
- The chat part's context keys move into agentsChatPartContext.ts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ows it The Chat view host service now reports the shown chat of the host that showed a chat last: the host that an open, start or reveal goes to (the Chat view for the chats a registered host leaves to it), or the host that restores or shows a chat on its own. New Chat of every icon variant now lets a host that replaces the Chat view start the new chat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tps://github.com/microsoft/vscode into sandy081/agents/chat-view-sessions-control # Conflicts: # src/vs/workbench/contrib/chat/test/browser/widgetHosts/viewPane/chatViewPane.test.ts
…rktree-locations' into sandy081/agents/editor-window-chat-part # Conflicts: # src/vs/workbench/contrib/chat/test/browser/widgetHosts/viewPane/chatViewPane.test.ts
As the Chat view does since #348304: a chat that the user opens while the chat part restores its tabs is opened, not restored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed tab As the Chat view reports the chat its first request commits: the session that a composer's first send created shows as opened, also in a composer tab that the chat part restored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s list to share The Sessions view and the chat part show sessions where the same holds: the sessions list of an editor window imports it from here instead of declaring its own. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rs offer Aligns AgentsChatViewHost.canShow with #348302's fall-through as agreed: besides agent host chats and the chats the window's sessions resolve, it also shows a session resolved by its resource and a chat of a type a provider offers, such as an untitled new session's. Local chat and the chats of other kinds stay in the Chat view. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ectories, and report an explicitly opened chat as opened Copilot code review fixes: - AgentHostWorktreeLocationProvider waits for the sessions list to resolve before reading the working directories of its sessions, so the worktrees of sessions that it did not cache are not missing. - The Chat view reports a chat that the user opens or reveals as opened, also one that it shows already, such as one it restored that the user picks in a sessions list (ChatViewPane.reportOpened, from the Chat view host's openSession and revealIfShowing). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Without a host that replaces it, the Chat view is always the host that showed a chat last, so the service subscribes to it only while a host is registered. Test stubs of the views service gain the view visibility event and the Chat view's shown chat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ns-control' into sandy081/agents/editor-window-chat-part # Conflicts: # src/vs/workbench/contrib/chat/browser/chatViewHost.ts # src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewHostService.ts
… screen readers The picker's accessible names do not include an item's description, so the folder that the window's folder is at now was announced by its name only. Its accessible name now includes "Current" (Copilot code review). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ers, and keeps the focus as it moves Copilot code review fixes: - The chat part also moves as its anchor view deregisters while its container stays, back to the primary side bar's side. - Moving the chat part to the other side of the editor takes it out of the DOM and back: the chat part gets back the focus it had. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A provided composer gains newChat(), which starts it over without its input, attachments and target. New Chat of the Chat view starts it over, and is the composer's while it shows in place of the chat; otherwise New Chat clears the chat, in whose place the composer then shows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sandy081/agents/editor-window-worktree-follow
…sandy081/agents/worktree-picker-groups
There was a problem hiding this comment.
🟡 Changes recommended
The picker exposes local folder/worktree choices for remote agent-host chats, but selecting them only updates the chip while the session ignores the requested working directory.
1 open issue
2 resolved since last review
Review effort: Balanced
…chats (CCR) A remote agent host chat in a local window resolves the requested working directory to none, so the chip's choice was ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ral folders are allowed (CCR) Which folder the worktree is of is not known then, so the first folder was named wrongly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…folder-location' into sandy081/agents/editor-window-worktree-follow
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because disabling worktree support can remove an open workspace-less session and dispose its content, leaving the chat read-only.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
…r-window-worktree-locations
…sandy081/agents/worktree-picker-groups
…r-window-worktree-follow
…groups' into sandy081/agents/editor-window-worktree-follow
…folder-location' into sandy081/agents/editor-window-worktree-follow
…e (CCR) A worktree that sessions work in through a symbolic link and through its real path showed twice in the folder picker. Locations are now deduplicated by their real path, keeping the first. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sandy081/agents/worktree-picker-groups
…ing New Worktree's folder (CCR) The section builder shows such a folder as a folder, so with it beside another folder, New Worktree from a worktree is named by the worktree, not by the other folder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…groups' into sandy081/agents/editor-window-worktree-follow
…te without moving The session layer's source swept every session, chat and workspace on each session update, such as a status, summary or git-state change of a busy session, inside the provider's transaction. It now reads every session again only as sessions are added or removed or a provider invalidates its list, and keys each chat and session by its working directories with strict equality, so an update that moves nothing stops at that chat's key. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…w object of it A committed draft replaces the draft's session object under the same id, reported only as changed. The session layer's source kept reading the replaced object until another session was added or removed; it now reads every session again as a changed session is not one it reads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ing and chats of a session it reads Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because a folder move during a pending pre-send save can retarget the composer and send the submitted prompt to the wrong checkout.
No open issues 🎉
1 resolved since last review
Review effort: Balanced


Stacked on #348304, #348298, #348294 and #348309. Review from the merges of those branches on. Merge order: #348304, #348298, #348294, #348309 (after #348292), then this PR.
It merges #348294 (375b387) because the feature moves the folder on its own: without #348294's folder location history, a listed or open chat of the primary folder would leave the sessions list (and its content be disposed) as the folder moves to a worktree.
Worktree support in an editor window with a single local folder, behind the experimental, experiment-controlled setting
chat.editorWindow.enableWorktree(off by default). It works on every surface that shows chats: the Chat view, with or without its sessions list, and a host that replaces it once that host reports the chat it shows (IChatViewHostService.shownSession). It depends on no feature that registers such a host.Changes
AgentHostWorktreeContribution(agentHostWorktree.contribution.ts, not in the Agents window), while the setting and AI features are on:AgentHostWorktreeLocationProvider, so the worktrees that local agent host sessions work in are locations of the folder, also for every picker that reads the folder's locations.ifIdle), and applies only while the user opened no chat since it started.editorWindow.worktreeFolderSwitchrecords how each switch ended. The setting'sexperimentTriggeris logged as a chat that the feature can follow (a local agent host session or a local chat) shows in a window whose folder can move, with the setting on or off. That moment precedes both the folder chip and any move of the folder.The Chat view's folder chip (
AgentHostWorktreeFolderChoices), for a new agent host session in a window with a single local folder:groupFolderLocationsfrom Workbench: worktree locations from agent host sessions, a shown-session seam and shared folder picker sections (infra, unused) #348304). Each section lists where the folder is now first, described as current.IAgentHostNewSessionFolderService).worktree, and the chip's label shows it. It is offered only where the session's isolation allows a worktree.chat.editorWindow.enableWorktree, boolean, defaultfalse,tags: ['experimental'],experiment: { mode: 'auto' }. It is an editor window UI setting, so it needs no runtime managed setting or policy.Review fixes
pnpm run typecheck-client: passedpnpm run valid-layers-check: passed./scripts/test.sh --runGlob 'vs/workbench/contrib/chat/test/browser/agentSessions/agentHost/**/*.test.js': 78 passingchatViewHostService,chatViewPane,folderLocationSections): 61 passing in the targeted runReview fixes, round 2
IChatViewHostOpenOptions.restore), so it keeps the folder there.shownSessionreasoncommit) follows on its own (ifIdle), so a move that the user started still wins.Decisions
ISessionReplacedEvent, from contrib/sessions, which contrib/chat may not import) is not carried over. Draft and untitled chats are never remembered, and the session that a first send commits is the one recorded: natively it is reported ascommit, and an extension's commit firesonDidCommitSession, which rebinds the entry. A remembered chat that a provider rebinds later leaves the sessions list, so its entry is forgotten. Moving to that location then opens nothing, rather than a wrong chat.pnpm run build-fast --client-only, then./scripts/test.sh --reporter dot --runGlob 'vs/workbench/contrib/chat/test/browser/agentSessions/agentHost/**/*.test.js': 85 passing./scripts/test.sh --reporter dot --run .../chatViewPane.test.ts --run .../chatViewHostService.test.ts --run .../folderLocationSections.test.ts: 47 passingagentHostWorktree.test.tsandagentHostWorktreeLocations.test.ts, and 1 failing inagentHostWorktreeFolderChoices.test.tspnpm run typecheck-client: passed;pnpm run valid-layers-check: passed; oxlint on the changed files: passedComposers of the editor window (stacked on #348309)
AgentHostWorktreeComposerFolders(contrib/sessions/browser/agentsView/agentHostWorktreeComposerFolders.ts, loaded fromworkbench.common.main.ts):IEditorWindowComposerFoldersService(Sessions: the chat part of editor windows, for any feature to enable #348309). It neither needs nor creates the chat part. The editor window's new-session composers then offer the window's folder, its own location and the worktrees that sessions work in: the chat part's composers, and the Chat view's once it registers its composer. They are presented as folder or worktree, with where the folder is now marked Current (IAllowedFolderPresentationfrom Sessions: group a composer's folders into Folder and Worktrees sections, with New Worktree first #348298).IEditorWindowComposerFoldersService.retargetComposerFolders(to, from)).createChatViewComposerModel). Only idle composers still aimed at the folder their host chose follow; one aimed at a folder the user picked, or one that is sending, stays (ChatWindowComposerModel.retargetFolder). Test: a folder move retargets both registered composers through the real service, without a chat part (agentHostWorktreeComposerFolders.test.ts, 11 passing).Review fixes, round 3
commit, both natively and through an extension's commit. Symbolic links of the repository's worktrees folder and of a worktree's.gitfile are followed.pnpm run build-fast --client-only):./scripts/test.sh --reporter dot --runGlob 'vs/workbench/contrib/chat/test/browser/agentSessions/agentHost/**/*.test.js': 92 passing--runGlob 'vs/workbench/contrib/sessions/test/browser/{agentsView,editorWindow,chatEditor}/**/*.test.js': 101 passing--runchatViewPane, chatViewHostService, chatWidgetService, chatMoveActions and folderLocationSections: 82 passingagentHostWorktree.test.tsandagentHostWorktreeComposerFolders.test.ts, and 3 failing inchatViewPane.test.tsandagentHostWorktreeLocations.test.ts(Workbench: worktree locations from agent host sessions, a shown-session seam and shared folder picker sections (infra, unused) #348304); all green afterpnpm run typecheck-client,pnpm run valid-layers-check, oxlint: passedNo Workspace and the two-line folder picker
chat.viewSessions.sessionsList.enabled) is on:IAgentHostNewSessionFolderService.isNoWorkspace/setNoWorkspace. Picking it is synchronous; the new session is created without a working directory, and it carries over a rebind of the provisional session. The chip shows the comment icon and "No Workspace", also for a restored session that has none (IAgentHostCustomizationService.isWorkspaceless).agent-host-folder-picker-listinagentHostChatInputPicker.css), so no other action list changes.getMinimumWidthmeasures its fixed parts and up to 160px of the name), and stays visible but disabled in a started session.<primary>.worktrees/<name>folder shows as a worktree only while it is pending, then as git proves it.isNoWorkspace, which fixed the Screenshots checks.Up to date
Merged Agent host sessions: keep sessions listed when switching a folder's location #348294 375b387 (36d7018, no conflicts). Its retention tests pass on this head:
./scripts/test.sh --reporter spec --run src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostChatContribution.test.ts --grep "keeps its sessions listed|keeps a session with multi-root": 2 passing ("switching the folder of a single-folder window to another location and back keeps its sessions listed", "a folder location switch keeps a session with multi-root metadata listed too, and Save Workspace As forgets the locations the folder had"). With the F4 suites plusagentHostChatContribution,editorCloudSandboxContribution,agentHostChatInputPickerandagentHostWorktree: 1617 passing;pnpm run typecheck-client: passed.Copilot code review: the worktree folder chip is offered only for local agent host chats (46793f6, red without it).
Merged
main9bfb9e7 through Workbench: worktree locations from agent host sessions, a shown-session seam and shared folder picker sections (infra, unused) #348304's branch (b132856, merge-only, no conflicts).Validation on b132856 (macOS):
pnpm run typecheck-client: passed;pnpm run build-fast --client-only, then./scripts/test.sh --reporter dotwith--runforagentHostWorktreeFolderChoices,agentHostFolderPickerActionItem,agentHostChatContribution,agentHostNewSessionFolderService,agentHostUntitledProvisionalSessionService,chatInputPart,agentHostWorktreeComposerFolders,agentHostWorktreeLocations,chatViewHostService,chatViewPaneandfolderLocationSections: 1360 passing.How to test
agentHostWorktree.test.ts:agentHostWorktreeFolderChoices.test.ts: the order and the current folder, at a worktree and at the folder; No Workspace; New Worktree and back to a folder; and when the choices are active.agentHostFolderPickerActionItem.test.ts(real DOM and CSS): the chip's label and minimum width in narrow and wide rows, No Workspace for a started or restored workspaceless session, and a two-line row with a description (the folder with Current) keeping its detail inside the row.Validation (macOS):
pnpm run typecheck-client: passedpnpm run valid-layers-check: passed./scripts/test.sh --reporter dot --run .../agentHostWorktreeFolderChoices.test.ts --run .../agentHostWorktree.test.ts --run .../agentHostWorktreeLocations.test.ts --run .../chatViewHostService.test.ts --run .../folderLocationSections.test.ts: 16 passing (and 18 withworkspaceFolderRepositories.test.tsbefore the chip commit)Not run: the full unit suite, and a live Code OSS run.
Final sweep
main776b518, Sessions: group a composer's folders into Folder and Worktrees sections, with New Worktree first #348298 1cf86c1 and Agent host sessions: keep sessions listed when switching a folder's location #348294 cf34efb (9ab4ecf, clean).pnpm run typecheck-client: passed;./scripts/test.sh --reporter dot --run <51 changed test files vs main>: 3449 passing, 4 pending.Performance: the session layer's source
A trace of an editor window with worktree support on, while a large session ran, showed the renderer busy re-reading every session, chat and workspace of the editor window's session layer on each update of a busy session (its status, summary or git state), inside the provider's transaction.
AgentHostWorktreeSessionLayerSourcenow reads every session again only as sessions are added or removed, as a provider invalidates what it lists, or as a provider replaces a session by a new object of it, such as a committed draft (reported as changed, under the same id).main(baseAgentHostSessionsProvider.ts), which has no equality check (a follow-up formain).getAllSessionsdoes not list, still makes it read every session again each time it is reported as changed.pnpm run typecheck-client: passed;./scripts/test.sh --reporter dot --run src/vs/workbench/contrib/sessions/test/browser/agentsView/agentHostWorktreeComposerFolders.test.ts: 18 passing; F4's 51 changed test files: 3453 passing, 4 pending.